# !/usr/bin/env python


import os
import datetime

from django.utils import simplejson
from google.appengine.ext import webapp
from google.appengine.ext.webapp import template
from google.appengine.ext.webapp import util
from google.appengine.api import users
from google.appengine.ext import db

import models

# Renders the main template.
class MainPage(webapp.RequestHandler):
  def get(self):
    current_user = users.GetCurrentUser()
    template_values = {
      'display_user': "user@host",
      'login_url': users.CreateLoginURL(self.request.uri),
      'logout_url': users.CreateLogoutURL(self.request.uri),
    }
    path = os.path.join(os.path.dirname(__file__), "showmap.html")
    self.response.out.write(template.render(path, template_values))


# Handles RPC requests
class RPCHandler(webapp.RequestHandler):
  """ Allows the functions defined in the RPCMethods class to be RPCed."""
  def __init__(self):
    webapp.RequestHandler.__init__(self)
    self.methods = RPCMethods()
 
  def get(self):
    func = None
   
    action = self.request.get('action')
    if action:
      if action[0] == '_':
        self.error(403) # access denied
        return
      else:
        func = getattr(self.methods, action, None)
   
    if not func:
      self.error(404) # file not found
      return
     
    args = ()
    while True:
      key = 'arg%d' % len(args)
      val = self.request.get(key)
      if val:
        args += (simplejson.loads(val),)
      else:
        break
    result = func(*args)
    self.response.out.write(simplejson.dumps(result))


class RPCMethods:
  """ Defines the methods that can be RPCed.
  NOTE: Do not allow remote callers access to private/protected "_*" methods.
  """
  def doGetResourceInformation(self, *args):
    devices = []
    dbDevices = db.GqlQuery("SELECT * FROM Device")
    for device in dbDevices:
      devices.append({"deviceId": device.deviceId, "groupId": device.groupId, "letterIdentifier": device.letterIdentifier})
    return devices
  
  def doGetCallInformation(self, *args):
    """Take lat, long and return the name and description of this call"""
    try:
      lat = float(args[0])
      long = float(args[1])
      latLng = ('%f,%f' % (lat, long))
    except:
      return None
    dbCalls = db.GqlQuery("SELECT * FROM Call WHERE location = :1", latLng)
    try:
      return {"test": "test2", "name": str(dbCalls[0].name), "description": str(dbCalls[0].description), }
    except IndexError: # New call, i.e lat/long does not exist in data source
      return {"name": "", "description": "Call Emergency",}
 
  def doUpdateMarkers(self, *args):
    mapMarkers = []
    resources = []
    dbCalls = db.GqlQuery("SELECT * FROM Call WHERE active = True")
    for call in dbCalls:
      latlng = str(call.location).partition(',') #Better way?
      mapMarkers.append({"call": 1, "x": latlng[0], "y": latlng[2], "name": str(call.name), "description": str(call.description)})

    dbDevices = db.GqlQuery("SELECT * FROM Device")
    for device in dbDevices:
      #TODO: See if we want this device included
      dbLocations = db.GqlQuery("SELECT * FROM Location WHERE deviceId = :1 ORDER BY dateTime ASC LIMIT 0,1", device.deviceId)
      for location in dbLocations:
        latlng = str(location.location).partition(',')
        mapMarkers.append({"call": 0, "x": latlng[0], "y": latlng[2], "letter": str(device.letterIdentifier), "deviceId": str(location.deviceId), "group": str(location.group)})
    
    return mapMarkers  
  
  def doHideCall(self, *args):
    """Take a latitude and longitude, and hide (active=0) all calls at this position"""
    try:
      lat = float(args[0])
      long = float(args[1])
      latLng = ('%f,%f' % (lat, long))
    except:
      return ("Last call removal FAILED and will continue to appear on other screens")
    call = models.Call(location=latLng)
    call.lastEdited = datetime.datetime.now()
    call.active = False
    call.put()
    self.deleteOldCalls(latLng, call.lastEdited)
    return ("Last call removal (Name %s) suceeded" % call.name)

  def deleteOldCalls(self, location, latestUpdate):
    q = db.GqlQuery("SELECT * FROM Call where location = :1 AND timeAdded < :2", location, latestUpdate)
    results = q.fetch(10) #>1 to catch any of the above queries that were missed
    try:
      db.delete(results)
    except IndexError: #no record to delete; no problem
      pass
  
  def doSaveCall(self, *args):
    """Take latitude, longitude, name and description and add a call at this position"""
    try:
      lat = float(args[0])
      long = float(args[1])
      name = str(args[2])
      description = str(args[3])
      latLng = ('%f,%f' % (lat, long))
    except:
      return ("<strong>Error:</strong> Last call addition (Name %s) FAILED and will not appear on other screens" % name)
    call = models.Call()
    call.location = latLng
    call.name = name
    call.description = description
    call.lastEdited = datetime.datetime.now()
    call.active=True
    call.put()
    self.deleteOldCalls(latLng, call.lastEdited)
    return ("Last call addition (Name %s) suceeded" % name)
 
def main():
  app = webapp.WSGIApplication([
    ('/', MainPage),
    ('/rpc', RPCHandler),
    ], debug=True)
  util.run_wsgi_app(app)

if __name__ == '__main__':
  main()